package fr.umlv.ig.model;

import java.awt.geom.Point2D;

/**
 * Represents a Train in a metro.
 * Train object is final with a public visibility. You can 
 * modified the object just if you are in his package.
 * @author Jenseric Catbagan
 * @author William Choy
 * 
 */
public class Train {
	/**Boolean to know if the station is in reverse of his itinerary.*/
	private boolean reverse = false;
	
	/**Boolean to know if the station is selected or not.*/
	private boolean isSelected;
	
	/**Static id which increments when a Train is create. */
	private static int idStatic=1;
	
	/**Id of the Train.*/
	private final int id;
	
	/**Itinerary of the train.*/
	private Itinerary itinerary;
	
	/**Last Station visited by the train.*/
	private Station currentStation;
	
	/**Next station which will be cisited by the train.*/
	private Station nextStation;
	
	/**Coordinate of the train.*/
	private Point2D coordinate;
	
	/**DirectorCoef using to calculate the coordinate of the train.*/
	private double directorCoef;
	
	/**Origin Orderly using to calculate the coordinate of the train.*/
	private double originOrdely;
	
	/**Distance traveled by the train between to stations*/
	private int currentNumberOfJump;
	
	/**Distance to traveled between to stations*/
	private int countJump;
	
	/**Boolean to know if the train is moving*/
	private boolean isMoving;
	
	/**distance traveled in abscissa coordinate by the train between two stations.*/
	private double distanceOfXJump;
	/**distance traveled in orderly by the train between two stations.*/
	private double distanceOfYJump=0;
	
	
	/**
	 * Initializes a Train with a itinerary.
	 * @param itinerary the itinerary of the train.
	 * @throws NullPointerException
	 *         the itinerary is null.
	 */
	public Train(Itinerary itinerary) {
		if( itinerary == null) {
			throw new NullPointerException("The itinerary is null");
		}
		this.id = idStatic++;
		this.itinerary = itinerary;
		isMoving=true;
		
		currentStation = itinerary.getStart();
		nextStation = currentStation.getNextStation(itinerary.getEnd());
		
		coordinate = new Point2D.Double((double)currentStation.getX(),(double)currentStation.getY());
		//coordinate.x = currentStation.getX();
		//coordinate.y = currentStation.getY();
		
		int x = nextStation.getX() - (int)coordinate.getX();
		int y = nextStation.getY() - (int)coordinate.getY();
		
		if(x ==0) {
			directorCoef = 0;
		}
		else {
			directorCoef = (double) y/x;
		}
		originOrdely = coordinate.getY() - (directorCoef*currentStation.getX());
		
	
		this.isMoving = true;

		currentNumberOfJump = 0;
		countJump = currentStation.getWaitingTime(nextStation);
		distanceOfXJump =  Math.abs(nextStation.getX()-coordinate.getX())/currentStation.getWaitingTime(nextStation);
		if(nextStation.getX() < coordinate.getX() ) {
			distanceOfXJump = -distanceOfXJump;
		}
		
		if( nextStation.getX() == coordinate.getX()) {
			distanceOfYJump =  Math.abs(nextStation.getY()-coordinate.getY())/currentStation.getWaitingTime(nextStation);
			if(nextStation.getY() < coordinate.getY() ) {
				distanceOfYJump = -distanceOfYJump;
			}
		}
		else {
			distanceOfYJump = 0;
		}
	}

	/**
	 * returns the id of the Train.
	 * @return the id of the Train.
	 */
	public int getId() {
		return id;
	}

	/**
	 * Sets the itinerary of the Train.
	 * @param itinerary the itinerary setted.
	 */
	void setItinary(Itinerary itinerary) {
		this.itinerary = itinerary;
	}

	/**
	 * Returns the itinerary of the train.
	 * @return the itinerary of the train.
	 */
	Itinerary getItinerary() {
		return itinerary;
	}

	
	/**
	 * Sets the current station of the train.
	 * @param currentStation the current station of the train.
	 */
	void setCurrentStation(Station currentStation) {
		this.currentStation = currentStation;
	}

	/**
	 * Returns the current station of the train.
	 * @return the current station of the train.
	 */
	Station getCurrentStation() {
		return currentStation;
	}

	/**
	 * Sets the next station of the train.
	 * @param nextStation the next station setted of the train.
	 */
	void setNextStation(Station nextStation) {		
		this.nextStation = nextStation;
	}
	
	/**
	 * Returns the next Station of the train.
	 * @return the next Station of the train.
	 */
	Station getNextStation() {
		return nextStation;
	
	}
	
	/**
	 * Called when a Train change state. If the train stopped to a station or
	 * if the train leave a station.
	 */
	private void setWay() {
		if(!nextStation.equals(getTerminus())) {
			nextStation.changedState(getTerminus());
		}
		currentStation = nextStation;
		if(reverse == false ) {
			if(currentStation.equals(itinerary.getEnd())) {
				reverse = true;
				nextStation = currentStation.getNextStation(itinerary.getStart());
				
			}
			else {
				nextStation = currentStation.getNextStation(itinerary.getEnd());
			}
		}
		else {
			if(currentStation.equals(itinerary.getStart())) {
				reverse = false;
				nextStation = currentStation.getNextStation(itinerary.getEnd());
			}
			else {
				nextStation = currentStation.getNextStation(itinerary.getStart());
			}
		}

	
		coordinate.setLocation(currentStation.getX(), currentStation.getY());
		
		int x = nextStation.getX() - (int)coordinate.getX();
		int y = nextStation.getY() - (int)coordinate.getY();
		if(x ==0) {
			directorCoef = 0;
		}
		else {
			directorCoef = (double) y/x;
		}
		originOrdely = coordinate.getY() - (directorCoef*currentStation.getX());
		
		currentNumberOfJump = 0;
		countJump = currentStation.getWaitingTime();
		distanceOfXJump =  Math.abs(nextStation.getX()-coordinate.getX())/currentStation.getWaitingTime(nextStation);
		if(nextStation.getX() < coordinate.getX() ) {
			distanceOfXJump = -distanceOfXJump;
		}
		
		if( nextStation.getX() == coordinate.getX()) {
			distanceOfYJump =  Math.abs(nextStation.getY()-coordinate.getY())/currentStation.getWaitingTime(nextStation);
			if(nextStation.getY() < coordinate.getY() ) {
				distanceOfYJump = -distanceOfYJump;
			}
		}
		else {
			distanceOfYJump = 0;
		}
		

	}
	
	/**
	 * Returns the coordinate of the train.
	 * @return the coordinate of the train.
	 */
	public Point2D getCoordinate() {
		return coordinate;
	}	
	
	/**
	 * Returns the line of the Train.
	 * @return the line of the Train.
	 */
	Line getLine() {
		return itinerary.getLine();
	}
	
	
	@Override
	public boolean equals(Object obj) {
		if(obj == this) {
			return true;
		}
		if(!(obj instanceof Train)) {
			return false;
		}
		Train t = (Train) obj;
		return id == t.id;
	}

	@Override
	public int hashCode() {
		return itinerary.hashCode();
	}
	
	/**
	 * Moves the train to the next station.
	 */
	public void move() {
		if(isMoving) {
			if(currentNumberOfJump >= countJump) {
				isMoving = false;
				setWay();
				currentNumberOfJump++;
				return;
			}
			double x = coordinate.getX() + distanceOfXJump;
			double y= coordinate.getY();
			if(distanceOfYJump!=0) {
				y += distanceOfYJump;
			}
			else {
				y = directorCoef*coordinate.getX() + originOrdely+distanceOfYJump;
			}
			coordinate.setLocation(x, y);
			
		}
		else {
			if(currentNumberOfJump >= countJump || !getCurrentStation().isOpened()) {
				if(!currentStation.equals(itinerary.getStart()) && 
						!currentStation.equals(itinerary.getEnd())) {
					currentStation.changedState(getTerminus());
				}

				countJump = currentStation.getWaitingTime(nextStation);
				currentNumberOfJump =0;
				isMoving = true;
//				coordinate.x = coordinate.x + distanceOfXJump;
//				coordinate.y = directorCoef*coordinate.x + originOrdely;	
				double x = coordinate.getX() + distanceOfXJump;
				double y = directorCoef*coordinate.getX() + originOrdely;
				coordinate.setLocation(x, y);
			}
		}
		currentNumberOfJump++;
	}
	
	/**
	 * Returns the terminus of the station.
	 * @return the terminus of the station.
	 */
	Station getTerminus() {
		if(reverse) {
			return itinerary.getStart();
		}
		return itinerary.getEnd();
	}
	
	/**
	 * Returns true if the train is moving, else false.
	 * @return true if the train is moving, else false.
	 */
	boolean isMoving() {
		return isMoving;
	}

	/**
	 * Returns true if the train is selected, else false.
	 * @return true if the train is selected, else false.
	 */
	 public boolean isSelected() {
		return isSelected;
	}
	
	 /**
	  * Sets the boolean which indicate if the station is selected.
	  * @param isSelected the boolean which indicate if the station is selected.
	  */
	public void setSelected(boolean isSelected) {
		this.isSelected = isSelected;
	}



}
